//= require <framework/w3c/components/Element>
//= require <framework/libs/templating/EcmaScriptTemplates>
//= require <framework/models/ComponentModel>
//= require <framework/w3c/traits/CSSStyleUtilities>
//= require <framework/w3c/traits/Draggable>
//= require <framework/w3c/traits/Selectables>
//= require <framework/w3c/traits/less-1.2.1.js>

namespace("w3c.HtmlComponent", {
    '@inherits': w3c.Element,
	'@traits'  : [w3c.CSSStyleUtilities],
	
	preInitialize : function(model, element) {
		try {
			var self=this;
			this.loadcss();
			this.createStyleDocument();
			this.setModel(model);
        	this.setElement(element);
			this.setSubElementReferences();
			this.initializeChildNodes();
			this.rerouteEvents();
			this.initializeTraits(arguments[0]);
			this.initialize(arguments[0]);
		} 
		catch(e){
			var msg = this.namespace + "#preInitialize() - " + e.message;
			if (console && console.error) {
				console.error(msg);
			}
			throw e;
		}
		return this;
    },
	
	setSubElementReferences : function(){
		var _childNodesWithIds = this.querySelectorAll("*[id]")||[];
		if(_childNodesWithIds && _childNodesWithIds.length > 0) {
			this.elements = [];	
		} else {return;}
		
		for(var i=0; i<=_childNodesWithIds.length-1; i++){
			var node    =_childNodesWithIds[i];
			if(!node || !node.getAttribute || node.nodeType != 1) { continue; }
			var nodeId = node.getAttribute("id");
			if(this.elements[nodeId]){
				if(console && console.warn) {
					console.warn("WARNING: " + this.namespace + "#storeIDs() raised a warning:\nThere are multiple childNodes with an ambigious node 'id' of: " + nodeId)
				}
			}
			else {
				this.elements[nodeId] =node;
			}
		}
	},
	
	initializeTraits : function(){
		var traits = this["@traits"];
		for(var i=0; i<=traits.length-1; i++){
			var trait = traits[i];
			if(trait && trait.initialize) {
				trait.initialize.call(this,null);
			}
		}
	},
	
	initialize : function(){},
	
	initializeChildNodes : function(){
		var self=this;
		this.components = {};
		var _childNodes = this.querySelectorAll("*[namespace]");
		_childNodes.forEach(function(node){
			if(!node || !node.getAttribute || node.nodeType != 1) { return };
			if(node.prorotype && node.prorotype instanceof w3c.HtmlComponent){return;};
			
		    var proto   = node.getAttribute("namespace");
			var containment = (node.getAttribute("containment")||"").toLowerCase();
			var Class   = NSRegistry[proto];
			
		    if (proto) {
				if (Class) {
					var cid = node.getAttribute("id");
					if (!containment || containment == "replace") {
						var component = new Class;
						if (node.parentNode) {
							node.parentNode.replaceChild(component.element, node);
							if (cid) {
								self.components[cid] = component;
							}
						}
					}
					else {
						if (containment && containment == "append") {
							var component = new Class;
							self.removeClass(component.classname, node);
							node.removeAttribute("namespace");
							node.removeAttribute("containment");
							node.appendChild(component.element);
							if (cid) {
								self.components[cid] = component;
							}
						}
						else {
							if (containment && containment == "skin") {
								var component2 = new Class({}, node);
								if (cid) {
									self.components[cid] = component2;
								}
							}
						}
					}
				}
				else {
					try{
						console.error("Missing Reference to JavaScript Class: " + proto + "\nExecuting Context: " + self.namespace + "#initializeChildNodes()\nNode:\n" + node.outerHTML);
					} catch(e){}
				}
			}
		})
	},
	
	rerouteEvents : function(){
		var self=this;
		this.addEventListener("mouseover", function(e){
			var relTarget = e.relatedTarget;
      		if (self.element === relTarget || self.hasChildNode(relTarget)){ return; }
			else{ if (self.mouseenter) { self.mouseenter(e) }}
		}, true);
		
		this.addEventListener("mouseout", function(e){
			var relTarget = e.relatedTarget;
      		if (self.element === relTarget || self.hasChildNode(relTarget)){ return; }
			else{ if (self.mouseleave) { self.mouseleave(e) }}
		}, true);
	},
	
	globalzindex:10,
	
	absoluteZindex : function(nodeReference){
		this.globalzindex = this.globalzindex + 1;
		return this.globalzindex;
	},
	
	indexOf : function(){
		var K = -1;
		var nodes = this.element.parentNode.childNodes;
		for (var i = nodes.length; i >= 0; i--) {
		    if (nodes[i] === this.element){
		        K = i;
		        break;
		    }
		}
		return K;
	},
	
	offsetParent : function(element){
		element = element||this.element;
		return element.offsetParent;
	},
	
	offsetLeft : function(obj) {
		obj = obj||this.element;
        $(obj).offset().left;
    },
	
	offsetTop : function(obj) {
		obj = obj||this.element;
        var curTop = 0;
        if (obj.offsetParent) {
            while (obj.offsetParent) {
                curTop += parseInt(obj.offsetTop,10);
                obj = obj.offsetParent;
            }
        }
        return curTop;
    },
	
	offsetX : function(obj) {
		return this.offsetLeft(obj) + application.scrollX();
    },
	
	setID : function(id){
		id = id||this.get("id");
		this.id = id;
		this.element.setAttribute("id", id);
	},
	
	innerHTML : "<div></div>",
	
	setModel : function(jsonobj) {
		jsonobj = jsonobj||this._model;
		this.model = (jsonobj && jsonobj instanceof jswing.models.ComponentModel) ?
			jsonobj : this.defineModel(jsonobj||{});
	},
	
	defineModel : function(json){
		return new jswing.models.ComponentModel(json||{});
	},
	
	setElement : function(element){
		this.element = (element || this._internal_parseElement());
		this.addClass(this.classname, this.element);
		this.setPrototypeInstance();	
		this.setNamespace();
		this.setUUID();
	},
	
	setUUID : function(){
		var uuid = Math.uuid(16);
		this.cuuid = uuid; // "c" for control
		this.element.setAttribute("cuuid", uuid);
	},
	
	setNamespace : function(){
		this.element.setAttribute("namespace", this.namespace);
	},
	
	getNamespace : function(){
		return this.element.getAttribute("namespace");
	},
	
	getPrototypeInstance : function(){
		return this.element.prototype;
	},
	
	setPrototypeInstance : function(){
		this.element.prototype = this;
	},
	
	get : function(key){
		return this.model.get(key)
	},
	
	set : function(key,val) {
		this.model.set(key,val);
	},
	
	parseTemplate : function (template, json) {
		if (typeof json !== "object") {
		    throw new Error(this.namespace + "#parseTemplate(template, json) expects a valid json object-literal for 2nd argument.");
		};
		return (TemplateEngine.parse(template,null,{$:json, self:this}) || "");
	},

	parseElement : function (template, json){
	     var html = this.parseTemplate(template, json);
		 if (html && html.length > 0) {return html.toHtmlElement()}
		 else {
		 	throw new Error(this.namespace + "#parseElement(template, json). Invalid xhtml generated from 'template' string. Value of 'html' is: "+ html);
		 }
	},
	
    resourcepath : function(filepath){
		/*var nspath = this.namespace.replace(/\./g,"/") + "/";
		if (filepath.indexOf("/") === 0 || filepath.indexOf("http://") === 0) {
			return filepath;
		}
		else {
			if (window.config.loadpaths.CDN && !(filepath.indexOf("http://") === 0)) {
				return window.config.loadpaths.CDN + nspath + filepath;
			}
			else {
				return config.loadpaths.LIBROOT + nspath + filepath;
			}
		}*/
		var nspath = this.namespace.replace(/\./g,"/") + "/";
		var isfile = /^[A-Za-z\w0-9\-\.\~\#\$\%\&\(\)\+\?]+\.css$/.test(filepath);
		
    },
	
	_internal_parseElement : function(){
		var templateString = (typeof this.innerHTML === "function") ?
			this.innerHTML() : this.innerHTML;
		return this.parseElement(templateString, {});
	},
	
	confirm : function(msg, callback, options){
		var node=null;
		if(Object.isElement(msg)) {
			node=msg;
		}
		else if(typeof msg === "string") {
			var node = document.createElement("div");
	        node.innerHTML = 
					"<div class='msg'>" + msg + "</div>\
	                 <span class='dismissal-controls'>\
					 	<span class='button button-ok flat-rounded-button'>OK</span>\
						<span class='button button-cancel flat-rounded-button'>CANCEL</span>\
					 </span>";
		};
		if(!node) {throw new Error(this.namespace + "#confirm(obj, callback, options) expected either a String or Node reference as the 1st param")};
		if(!this.hasClass("ConfirmDialog", node)) {
			this.addClass("ConfirmDialog",node);
		};
		
        var m = new controls.ModalDialog(node, callback, options||{});
		m.show();
	},
	
	//invoked by w3c/Selectable during a drag-select operation 
	select : function(){
		this.addClass("selected");
	},
	
	deselect : function(){
		this.removeClass("selected");
		this.removeClass("drag-selected");
	},
	
	resetzindex : function(){
		this.element.style["zIndex"] = 0;
	},
	
	cloneNode : function() {
		var elm   = this.element.cloneNode(true);
		var model = js.extend({},this.model.model);
		var clone = new this.constructor(model||{},elm);
		return clone;
	},
	
	animate: function(css, options){
		var o = js.extend({
			queue: false,
	 		duration: 500,
			easing:"sineEaseIn",
			callback:function(){}
		}, options||{});
		
	 	var element = o.element||this.element;
	 	$(element).animate(css, o.duration, o.easing, o.callback);
	 }
});